﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using MugenInjection.Core;
using MugenInjection.Interface.Components;

namespace MugenInjection.Interface
{
    /// <summary>
    /// Represents the ioc container, that can create objects of all kinds.
    /// </summary>
    public interface IInjector : IInjectorResolver, IInjectorBinder
    {
        /// <summary>
        /// Gets the id of <see cref="IInjector"/>.
        /// </summary>
        int Id { get; }

        /// <summary>
        /// Gets the collection of all modules for current <see cref="IInjector"/>.
        /// </summary>
        ReadOnlyCollection<IInjectorModule> Modules { get; }

        /// <summary>
        /// Gets the collection of children.
        /// </summary>
        ReadOnlyCollection<IInjector> ChildInjectors { get; }

        /// <summary>
        /// Gets the configuration options for current <see cref="IInjector"/>.
        /// </summary>
        IInjectorSettings Settings { get; }

        /// <summary>
        /// Gets the collection of all components for current <see cref="IInjector"/>.
        /// </summary>
        IComponentContainer Components { get; }

        /// <summary>
        /// Gets the root <see cref="IInjector"/>.
        /// </summary>
        /// <returns>An instance of <see cref="IInjector"/>.</returns>
        IInjector GetRoot();

        /// <summary>
        /// Gets the parent <see cref="IInjector"/>.
        /// </summary>
        /// <returns>An instance of <see cref="IInjector"/>.</returns>
        IInjector GetParent();

        /// <summary>
        /// Sets the specified <see cref="IInjector"/> as a parent, if the <see cref="IInjector"/> already has a parent will be thrown an <see cref="ArgumentException"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        void SetParent(IInjector injector);

        /// <summary>
        /// Creates a managed scope.
        /// </summary>
        /// <returns>An instance of <see cref="IManagedScope"/>.</returns>
        IManagedScope CreateManagedScope();

        /// <summary>
        /// Creates a child <see cref="IInjector"/> for the current <see cref="IInjector"/>.
        /// </summary>
        /// <param name="settings">The specified settings for the child <see cref="IInjector"/>.</param>
        /// <param name="componentContainer">The specified <see cref="IComponentContainer"/>. </param>
        /// <param name="cloneComponents">The specifies whether to clone the components from the parent.</param>
        /// <param name="components">The specified <see cref="IComponent"/> for new <see cref="IInjector"/></param>
        /// <param name="modules">The specified <see cref="InjectorModule"/> for new <see cref="IInjector"/></param>
        /// <returns>An instance of <see cref="IInjector"/>.</returns>
        IInjector CreateChild(IInjectorSettings settings, IComponentContainer componentContainer, bool cloneComponents,
                              IList<IComponent> components, params IInjectorModule[] modules);

        /// <summary>
        /// Adds the specified <see cref="IInjector"/> in the collection of <c>ChildInjectors</c>, if the <see cref="IInjector"/> already has a parent will be thrown an <see cref="ArgumentException"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        void AddChild(IInjector injector);

        /// <summary>
        /// Removes the child <see cref="IInjector"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        void RemoveChild(IInjector injector);

        /// <summary>
        /// Determines whether a module with the specified name has been loaded in the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="name">The specified module name.</param>
        /// <returns><c>True</c> if the specified module has been loaded; otherwise, <c>false</c>.</returns>
        bool ModuleIsLoaded(string name);

        /// <summary>
        /// Determines whether a module with the specified <see cref="Type"/> has been loaded in the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="moduleType">Module <see cref="Type"/>.</param>
        /// <returns><c>True</c> if the specified module has been loaded; otherwise, <c>false</c>.</returns>
        bool ModuleIsLoaded(Type moduleType);

        /// <summary>
        /// Creates a new instance of the module and loads it into the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="moduleType">The type of the module.</param>
        void Load(Type moduleType);

        /// <summary>
        /// Loads the module into the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="module">The specified <see cref="InjectorModule"/>.</param>
        void Load(IInjectorModule module);

        /// <summary>
        /// Unloads the module with the specified name.
        /// </summary>
        /// <param name="name">The specified module name.</param>
        void Unload(string name);

        /// <summary>
        /// Unloads the module with the specified <see cref="Type"/>.
        /// </summary>
        /// <param name="moduleType">The specified module <see cref="Type"/>.</param>
        void Unload(Type moduleType);

        /// <summary>
        /// Releases specified service with specified condition in scope.
        /// </summary>
        /// <param name="bindingContext">The specified <see cref="IBindingContext"/>.</param>
        /// <param name="needDispose">Try dispose object.</param>
        /// <returns><c>True</c> if the specified service has been release; otherwise, <c>false</c>.</returns>
        bool Release(IBindingContext bindingContext, bool needDispose);

        /// <summary>
        /// Releases all specified service in scope.
        /// </summary>
        /// <param name="service">The specified service <see cref="Type"/> for release.</param>
        /// <param name="needDispose">Try dispose object.</param>
        /// <returns><c>True</c> if the specified service has been release; otherwise, <c>false</c>.</returns>
        bool ReleaseAll(Type service, bool needDispose);
    }
}